/************************************************************************
 File:			Ethernet.cpp
 Author:		Justin England
 Class:			CSC 470
 Instructor:	Dr. Penaloza
 Date:
 Description:	Class file for ethernet representation.
 
 Known bugs/missing features:
 Modifications:
 Date                Comment            
 ----    ------------------------------------------------
 ************************************************************************/

#include <QtGui>
#include <QtDebug>
#include "Ethernet.h"
#include "Master.h"

/************************************************************************
 Function:		Ethernet()
 Author:		Justin England
 
 Description:	This is the constructor for the ethernet class.  It
				is passed the parameters needed to run the simulation
				with the options selected within the GUI.
 
				Creates X number of master ojects (X selected from GUI)
				and that same number of threads for each master oject.

 Parameters:	<**** ADD PARAMS THAT ARE USED IN SIM ****>
 
 ************************************************************************/

Ethernet::Ethernet(QObject *parent) : QObject(parent)
{
	theNet.masters		= 100;			// number of masters *** change this to be set from calling function ***

	theNet.collisions	= 0;			// initalize collisions counter
	theNet.packets		= 0;			// initalize totalPackets counter
	theNet.currentState = Idle;			// initalize currentState to Idle

	
	// create theNet.masters (the number of masters) objects
	// and a corressponding thread for each master object
	
	for(int i = 0; i < theNet.masters; i++)
	{
		// create a new master object and append to netMasters list, 
		// passing it's master # (the i value) and the total number of masters
		netMasters.append(new Master(i, theNet.masters));
		
		// attach the master to this ethernet object by passing
		// the 'this' pointer
		netMasters.at(i)->attachToEthernet(this);
		
		// create a new thread and append to masterThreads list
		masterThreads.append(new QThread());
		
		// move the master ojbect to the newly create thread
		netMasters.at(i)->moveToThread(masterThreads.at(i));
		
		// tell the thread to start executing
		masterThreads.at(i)->start();
	}
	
	// tell the masters to start generating packets
	start();	// *** THIS WILL BE CALLED FROM A SIGNAL FROM GUI ***
}


/************************************************************************
 Function:		~Ethernet()
 Author:		Justin England
 
 Description:	Destructor for ethernet oject.  Loops through each
				master thread and terminates them
 Parameters:
 ************************************************************************/

Ethernet::~Ethernet()
{
	for (int i = 0; i < theNet.masters; i++)
	{
		// end all threads
		netMasters.at(i)->sendPacketTimer->stop();
		masterThreads.at(i)->quit();
	}
}

/************************************************************************
 Function:		start()
 Author:		Justin England
 
 Description:	This method starts the ethernet simulation.  It loops
				through each master object and calls their start()
				method.  Therefore, each master will start generating
				packets to send to the ethernet.
 
 Parameters:	<none>
 
 ************************************************************************/

void Ethernet::start(void)
{
	for(int i = 0; i < theNet.masters; i++)
		netMasters.at(i)->start();
}

/************************************************************************
 Function:		start()
 Author:		Justin England
 
 Description:	This method stops all masters from sending packets to
				the ethernet
 
 Parameters:	<none>
 
 ************************************************************************/

void Ethernet::stop(void)
{
}

/************************************************************************
 Function:		isBusy()
 Author:		Justin England
 
 Description:	returns TRUE if the ethernet is currently busy,
				FALSE if it is available
 
 Parameters:	<none>
 Returns:		boolean value - see description
 
 ************************************************************************/

bool Ethernet::isBusy(void)
{
	// returns the state of the ethernet to calling master
	// used to determine if the ethernet is available to
	// receive a packet from sending master
	
	if (theNet.currentState == Idle)
	{
		return FALSE;
	}
	
	return TRUE;
}

/************************************************************************
 Function:		putOnEthernet()
 Author:		Justin England
 
 Description:	This receives a "packet" from a master object and 
				puts it into the ethernet queue
 
 Parameters:	int packet	- an int value that represents a packet
							- generated randomly by master object
 
 ************************************************************************/

void Ethernet::putOnEthernet(int packet)
{
	// this method does nothing more than add a "packet" to a queue
	// -- there should only be one packet in the queue at any given
	// -- time, if there are more than one packet (as determined in 
	// -- the acceptPacket method,) a collision occured
	
	packetMutex.lock();				// lock mutex to aviod concurrent access
	dataPacket.append(packet);		// add packet to queue
	packetMutex.unlock();			// unlock mutex
}

/************************************************************************
 Function:		acceptPacket()
 Author:		Justin England
 
 Description:	Requested from master object to determine if the packet
				was successfully sent to the ethernet or if a collision
				occured.
 
 Parameters:	int sender		- the ID of the sending master
				int receiver	- the ID of the receiving master
 
 Returns:		bool	TRUE	- the packet was sent successfully
						FALSE	- a collision occured on the ethernet
 
 ************************************************************************/

bool Ethernet::acceptPacket(int sender, int receiver /* packetSize */)
{
	// this will lock all data that is modified within this method
	// and the MutexLocker will release the lock once it goes out
	// of scope (at the return locations);  the mutexlocker
	// makes it easier to lock / unlock the mutex
	QMutexLocker locker(&packetMutex);

	// check if there are multiple packets to process
	// if so, there is a collision, record and return false
	// to calling master so that it's aware there was a
	// collision
	if(dataPacket.size() > 1)		// collision occured
	{
		
		dataPacket.clear();			// clear ethernet
		theNet.collisions++;		// increment collision counter
		return FALSE;				// return FASLE to calling master
		
	}
	
	// since the only way the dataPacket queue can be empty is if
	// another thread cleared it (see above) therefore, also notify this
	// calling thread that a collision occured.  IE  For a collision
	// to occur, two masters are sending at the same time, the first
	// was notified above, now we notify the second master
	// Also note, the since this is the ethernet object, only one collision
	// occured, therefore, we only update the counter once (as done above)
	if(dataPacket.isEmpty())
		return FALSE;				// return FALSE to calling master
	
	// no collision occured, process packet
	theNet.currentState = Busy;		// set ethernet state to busy
	theNet.packets++;				// increment packet count
	
	// process packet speed / data size
	// this should have some algroithm to create a delay based 
	// off the packet size and the speed;  The packet size could be
	// a function of the random INT that is found in the dataPacket queue
	for (int i = 0; i < 10000; i++) {}
	
	netMasters.at(receiver)->rxPacket();	// notify receiver of packet
	theNet.currentState = Idle;				// set ethernet back to idle
	dataPacket.clear();						// clear packet from ethernet

	return TRUE;							// packet successful
}



